# Copyright (c) 2015, Frappe Technologies Pvt. Ltd. and Contributors
# License: GNU General Public License v3. See license.txt
import frappe
from frappe import _
from frappe.utils import cint, flt
from erpnext.accounts.report.financial_statements import (
	compute_growth_view_data,
	get_columns,
	get_data,
	get_filtered_list_for_consolidated_report,
	get_period_list,
)

def execute(filters=None):
	filters = frappe._dict(filters or {})
	
	# 生成期间列表
	period_list = get_period_list(
		filters.from_fiscal_year,
		filters.to_fiscal_year,
		filters.period_start_date,
		filters.period_end_date,
		filters.filter_based_on,
		filters.periodicity,
		company=filters.company,
	)
	filters.period_start_date = period_list[0]["year_start_date"]
	
	# 获取币种信息
	currency = filters.presentation_currency or frappe.get_cached_value(
		"Company", filters.company, "default_currency"
	)
	
	# 获取资产、负债、权益数据
	asset = get_data(
		filters.company,
		"Asset",
		"Debit",
		period_list,
		only_current_fiscal_year=False,
		filters=filters,
		accumulated_values=filters.accumulated_values,
	)
	liability = get_data(
		filters.company,
		"Liability",
		"Credit",
		period_list,
		only_current_fiscal_year=False,
		filters=filters,
		accumulated_values=filters.accumulated_values,
	)
	equity = get_data(
		filters.company,
		"Equity",
		"Credit",
		period_list,
		only_current_fiscal_year=False,
		filters=filters,
		accumulated_values=filters.accumulated_values,
	)
	
	# 应用新模板映射与排序（自动按Reporting Item分组）
	template = filters.get("balance_sheet_template")
	if template:  # 缩进与外层保持一致（4个空格）
		mapping = get_balance_sheet_mapping(template)
		if mapping:
			asset = apply_template_mapping(asset, mapping, "Asset")
			liability = apply_template_mapping(liability, mapping, "Liability")
			equity = apply_template_mapping(equity, mapping, "Equity")

	# 计算临时损益和校验期初余额
	provisional_profit_loss, total_credit = get_provisional_profit_loss(
		asset, liability, equity, period_list, filters.company, currency
	)
	message, opening_balance = check_opening_balance(asset, liability, equity)
	
	# 处理Fiscal Year Opening逻辑（适配新模板）
	if filters.get("fiscal_year_opening"):
		fy_start_date = period_list[0]["year_start_date"]
		opening_period_list = get_period_list(
			filters.from_fiscal_year,
			filters.from_fiscal_year,
			fy_start_date,
			fy_start_date,
			filters.filter_based_on,
			"Yearly",
			company=filters.company,
		)
		opening_asset = get_data(
			filters.company,
			"Asset",
			"Debit",
			opening_period_list,
			only_current_fiscal_year=False,
			filters=filters,
			accumulated_values=0,
		)
		opening_liability = get_data(
			filters.company,
			"Liability",
			"Credit",
			opening_period_list,
			only_current_fiscal_year=False,
			filters=filters,
			accumulated_values=0,
		)
		opening_equity = get_data(
			filters.company,
			"Equity",
			"Credit",
			opening_period_list,
			only_current_fiscal_year=False,
			filters=filters,
			accumulated_values=0,
		)
		# 应用模板映射到期初数据
		if template and mapping:
			opening_asset = apply_template_mapping(opening_asset, mapping, "Asset")
			opening_liability = apply_template_mapping(opening_liability, mapping, "Liability")
			opening_equity = apply_template_mapping(opening_equity, mapping, "Equity")
		merge_opening_data(asset, opening_asset, opening_period_list[0].key)
		merge_opening_data(liability, opening_liability, opening_period_list[0].key)
		merge_opening_data(equity, opening_equity, opening_period_list[0].key)
	
	# 组装主数据及后续逻辑（保持不变）
	data = []
	data.extend(asset or [])
	data.extend(liability or [])
	data.extend(equity or [])
	
	if opening_balance and round(opening_balance, 2) != 0:
		unclosed = {
			"account_name": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
			"account": "'" + _("Unclosed Fiscal Years Profit / Loss (Credit)") + "'",
			"warn_if_negative": True,
			"currency": currency,
		}
		for period in period_list:
			unclosed[period.key] = opening_balance
			if provisional_profit_loss:
				provisional_profit_loss[period.key] = provisional_profit_loss[period.key] - opening_balance
		unclosed["total"] = opening_balance
		data.append(unclosed)
	
	if provisional_profit_loss:
		data.append(provisional_profit_loss)
	if total_credit:
		data.append(total_credit)
	
	columns = get_columns(
		filters.periodicity, period_list, filters.accumulated_values, company=filters.company
	)
	if filters.get("fiscal_year_opening"):
		columns.insert(2, {
			"fieldname": "opening_balance",
			"label": _("Fiscal Year Opening"),
			"fieldtype": "Currency",
			"currency": currency,
			"width": 140
		})
	
	chart = get_chart_data(filters, columns, asset, liability, equity, currency)
	report_summary, primitive_summary = get_report_summary(
		period_list, asset, liability, equity, provisional_profit_loss, currency, filters
	)
	
	if filters.get("selected_view") == "Growth":
		compute_growth_view_data(data, period_list)
	
	return columns, data, message, chart, report_summary, primitive_summary

def get_balance_sheet_mapping(template):
	"""从Balance Sheet Config CN获取account与reporting_item的映射及sort_id"""
	if not template:
		return {}
	
	# 先检查模板是否存在
	if not frappe.db.exists("Balance Sheet Config CN", template):
		frappe.log_error(f"模板不存在: {template}", "Balance Sheet Mapping Error")
		return {}
	
	# 执行查询时增加错误捕获
	try:
		# 使用as_dict=True确保返回字典格式，明确指定表名和字段名
		mapping_items = frappe.db.sql("""
			SELECT 
				`tabBalance Sheet Config Item CN`.`account` AS account,
				`tabBalance Sheet Config Item CN`.`reporting_item` AS reporting_item,
				`tabBalance Sheet Config Item CN`.`sort_id` AS sort_id,
				`tabBalance Sheet Config Item CN`.`account_type` AS account_type
			FROM `tabBalance Sheet Config Item CN`
			WHERE `tabBalance Sheet Config Item CN`.`parent` = %s
			AND `tabBalance Sheet Config Item CN`.`account` IS NOT NULL  # 过滤空值
			ORDER BY `tabBalance Sheet Config Item CN`.`sort_id` ASC
		""", (template), as_dict=True)
		
		# 调试：记录查询结果
		frappe.log_error(
			f"查询到{len(mapping_items)}条映射记录，模板: {template}",
			"Balance Sheet Mapping Debug"
		)
		
		# 构建映射，再次检查account字段
		mapping = {}
		for item in mapping_items:
			account = item.get("account")
			if account:
				mapping[account] = item
			else:
				frappe.log_error(
					f"发现无效记录（account为空）: {str(item)}",
					"Balance Sheet Mapping Invalid Record"
				)
		
		return mapping
	
	except Exception as e:
		frappe.log_error(
			f"查询映射时出错: {str(e)}，模板: {template}",
			"Balance Sheet Mapping Query Error"
		)
		return {}

def apply_template_mapping(data, mapping, account_type):
    """按Reporting Item分组重组数据，构建层级结构：
    - 已映射账户按配置分组
    - 未映射的明细科目（非父账户）归到"undefined"
    - 未映射的父账户（is_group=True）直接过滤
    - 父项（Reporting Item）显示子项金额的汇总
    """
    if not data:
        return []
    
    # 1. 分组：按Reporting Item归类
    grouped = {}
    for row in data:
        account = row.get("account")
        is_group = row.get("is_group", False)  # 判断是否为父账户
        
        # 检查是否有有效映射
        if account in mapping and mapping[account]["account_type"] == account_type:
            reporting_item = mapping[account]["reporting_item"] or "undefined"
            sort_id = mapping[account]["sort_id"]
        else:
            # 未映射的父账户直接过滤
            if is_group:
                continue  # 跳过父账户
            # 未映射的明细科目归到undefined
            reporting_item = "undefined"
            sort_id = 9999  # 未映射项放末尾
        
        # 初始化分组
        if reporting_item not in grouped:
            grouped[reporting_item] = {
                "sort_id": sort_id,
                "children": []
            }
        
        # 处理子项（原Account数据）
        child_row = row.copy()
        child_row["reporting_item"] = reporting_item
        child_row["sort_id"] = sort_id
        child_row["is_group"] = False
        child_row["parent"] = reporting_item
        grouped[reporting_item]["children"].append(child_row)
    
    # 2. 排序：先按分组的sort_id，再按子项的sort_id
    sorted_groups = sorted(grouped.items(), key=lambda x: x[1]["sort_id"])
    
    # 3. 构建最终数据结构（父项+子项，父项汇总子项金额）
    result = []
    for reporting_item, group_data in sorted_groups:
        # 计算当前分组下所有子项的金额总和
        totals = {}
        for child in group_data["children"]:
            # 遍历子项的所有期间字段（排除非金额字段）
            for key, value in child.items():
                if key in ("account_name", "reporting_item", "is_group", "parent", "sort_id", "indent", "account"):
                    continue  # 跳过非金额字段
                # 累加金额（处理浮点数）
                totals[key] = flt(totals.get(key, 0)) + flt(value)
        
        # 父项：Reporting Item（包含汇总金额）
        parent_row = {
            "account_name": reporting_item,
            "reporting_item": reporting_item,
            "is_group": True,
            "parent": "",
            "sort_id": group_data["sort_id"],
            "indent": 0,
            **totals  # 合并汇总的金额字段
        }
        # 继承子项的currency属性（如果存在）
        if group_data["children"]:
            parent_row["currency"] = group_data["children"][0].get("currency")
        
        result.append(parent_row)
        
        # 子项：对应Account（按sort_id排序）
        sorted_children = sorted(group_data["children"], key=lambda x: x.get("sort_id", 9999))
        for child in sorted_children:
            child["indent"] = 1
            result.append(child)
    
    return result

# 保持其他函数不变
def get_provisional_profit_loss(asset, liability, equity, period_list, company, currency=None, consolidated=False):
	# 保持原生逻辑不变
	provisional_profit_loss = {}
	total_row = {}
	if asset:
		total = total_row_total = 0
		currency = currency or frappe.get_cached_value("Company", company, "default_currency")
		total_row = {
			"account_name": "'" + _("Total (Credit)") + "'",
			"account": "'" + _("Total (Credit)") + "'",
			"warn_if_negative": True,
			"currency": currency,
		}
		has_value = False
		for period in period_list:
			key = period if consolidated else period.key
			total_assets = flt(asset[-2].get(key))
			effective_liability = 0.00
			if liability and liability[-1] == {}:
				effective_liability += flt(liability[-2].get(key))
			if equity and equity[-1] == {}:
				effective_liability += flt(equity[-2].get(key))
			provisional_profit_loss[key] = total_assets - effective_liability
			total_row[key] = provisional_profit_loss[key] + effective_liability
			if provisional_profit_loss[key]:
				has_value = True
			total += flt(provisional_profit_loss[key])
			provisional_profit_loss["total"] = total
			total_row_total += flt(total_row[key])
			total_row["total"] = total_row_total
		if has_value:
			provisional_profit_loss.update(
				{
					"account_name": "'" + _("Provisional Profit / Loss (Credit)") + "'",
					"account": "'" + _("Provisional Profit / Loss (Credit)") + "'",
					"warn_if_negative": True,
					"currency": currency,
				}
			)
	return provisional_profit_loss, total_row

def check_opening_balance(asset, liability, equity):
	# 保持原生逻辑不变
	opening_balance = 0
	float_precision = cint(frappe.db.get_default("float_precision")) or 2
	if asset:
		opening_balance = flt(asset[-1].get("opening_balance", 0), float_precision)
	if liability:
		opening_balance -= flt(liability[-1].get("opening_balance", 0), float_precision)
	if equity:
		opening_balance -= flt(equity[-1].get("opening_balance", 0), float_precision)
		opening_balance = flt(opening_balance, float_precision)
	if opening_balance:
		return _("Previous Financial Year is not closed"), opening_balance
	return None, None

def get_report_summary(period_list, asset, liability, equity, provisional_profit_loss, currency, filters, consolidated=False):
	# 保持原生逻辑不变
	net_asset, net_liability, net_equity, net_provisional_profit_loss = 0.0, 0.0, 0.0, 0.0
	if filters.get("accumulated_values"):
		period_list = [period_list[-1]]
	if filters.get("accumulated_in_group_company"):
		period_list = get_filtered_list_for_consolidated_report(filters, period_list)
	for period in period_list:
		key = period if consolidated else period.key
		if asset:
			net_asset += asset[-2].get(key)
		if liability and liability[-1] == {}:
			net_liability += liability[-2].get(key)
		if equity and equity[-1] == {}:
			net_equity += equity[-2].get(key)
		if provisional_profit_loss:
			net_provisional_profit_loss += provisional_profit_loss.get(key)
	return [
		{"value": net_asset, "label": _("Total Asset"), "datatype": "Currency", "currency": currency},
		{
			"value": net_liability,
			"label": _("Total Liability"),
			"datatype": "Currency",
			"currency": currency,
		},
		{"value": net_equity, "label": _("Total Equity"), "datatype": "Currency", "currency": currency},
		{
			"value": net_provisional_profit_loss,
			"label": _("Provisional Profit / Loss (Credit)"),
			"indicator": "Green" if net_provisional_profit_loss > 0 else "Red",
			"datatype": "Currency",
			"currency": currency,
		},
	], (net_asset - net_liability + net_equity)

def get_chart_data(filters, columns, asset, liability, equity, currency):
	# 保持原生逻辑不变
	labels = [d.get("label") for d in columns[2:]]
	asset_data, liability_data, equity_data = [], [], []
	for p in columns[2:]:
		if asset:
			asset_data.append(asset[-2].get(p.get("fieldname")))
		if liability:
			liability_data.append(liability[-2].get(p.get("fieldname")))
		if equity:
			equity_data.append(equity[-2].get(p.get("fieldname")))
	datasets = []
	if asset_data:
		datasets.append({"name": _("Assets"), "values": asset_data})
	if liability_data:
		datasets.append({"name": _("Liabilities"), "values": liability_data})
	if equity_data:
		datasets.append({"name": _("Equity"), "values": equity_data})
	chart = {"data": {"labels": labels, "datasets": datasets}}
	if not filters.accumulated_values:
		chart["type"] = "bar"
	else:
		chart["type"] = "line"
	chart["fieldtype"] = "Currency"
	chart["options"] = "currency"
	chart["currency"] = currency
	return chart

def merge_opening_data(main_data, opening_data, opening_key):
	# 保持原生逻辑不变
	if not opening_data:
		return
	opening_map = {row.get("account"): row for row in opening_data if "account" in row}
	for row in main_data:
		account = row.get("account")
		if account in opening_map:
			opening_row = opening_map[account]
			row["opening_balance"] = opening_row.get(opening_key, 0)